/*
 * Copyright (c) 2008-2011, Petr Panteleyev
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *    3. The name of the author may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.panteleyev.blogclient;

import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.EventObject;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

import org.panteleyev.blogapi.IBlogEntry;
import org.panteleyev.blogclient.events.PostDeletedEvent;
import org.panteleyev.swing.BaseFrame;
import org.panteleyev.swing.OSUtil;
import org.panteleyev.swing.WindowManager;
import org.panteleyev.swing.WindowManagerEnumerator;
import org.panteleyev.swing.events.CloseFrameEvent;
import org.panteleyev.swing.filefilters.FileFilter;
import org.panteleyev.utilities.events.AppEventListener;

/**
 *
 * @param <T>
 * @author Petr Panteleyev
 */
public abstract class BlogPostEditorFrame<T extends UserProfile<E>, E extends IBlogEntry> extends BloggerEditorFrame<T> {
    // Actions
    private AbstractAction publishAction;
    private AbstractAction publishAndContinueAction;
    private AbstractAction publishLaterAction;
    private AbstractAction previewEntryAction;
    private AbstractAction deleteEntryAction;
    private AbstractAction updateTagsAction;

    private File saveFile;

    public BlogPostEditorFrame(T profile) {
        super(profile);
        createActions();

        addAppEventListener(PostDeletedEvent.class, new AppEventListener() {
            @Override
            public void eventReceived(EventObject event) {
                onPostDeletedEvent((PostDeletedEvent)event);
            }
        });
    }

    public File getSaveFile() {
        return saveFile;
    }

    public void setSaveFile(File saveFile) {
        this.saveFile = saveFile;
    }

    private void createActions() {
        publishAction = new AbstractAction(L10N.POST_WINDOW_PUBLISH_ACTION.s()) {
            @Override
            public void actionPerformed(ActionEvent e) {
                onPublish();
            }
        };
        publishAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_P, BloggingKit.getKeyMask()));

        publishAndContinueAction = new AbstractAction(L10N.POST_WINDOW_PUBLISH_AND_CONTINUE_ACTION.s()) {
            @Override
            public void actionPerformed(ActionEvent e) {
                onPublishAndContinue();
            }
        };
        publishAndContinueAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_P, InputEvent.SHIFT_MASK | BloggingKit.getKeyMask()));

        publishLaterAction = new AbstractAction("Publish Later") {
            @Override
            public void actionPerformed(ActionEvent e) {
                onPublishLater();
            }
        };

        previewEntryAction = new AbstractAction(L10N.POST_WINDOW_PREVIEW_ACTION.s()) {
            @Override
            public void actionPerformed(ActionEvent e) {
                onPreviewEntry();
            }
        };
        previewEntryAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.SHIFT_MASK | BloggingKit.getKeyMask()));


        deleteEntryAction = new AbstractAction(L10N.POST_WINDOW_DELETE_ACTION.s()) {
            @Override
            public void actionPerformed(ActionEvent e) {
                onDeleteEntry();
            }
        };
        deleteEntryAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, BloggingKit.getKeyMask()));

        updateTagsAction = new AbstractAction(null, Icon.RELOAD.i()) {
            @Override
            public void actionPerformed(ActionEvent e) {
                onUpdateTags();
            }
        };
    }

    protected JMenu createEntryMenu() {
        publishAction.setEnabled(getProfile().isOnline());
        publishAndContinueAction.setEnabled(getProfile().isOnline());

        return createMenu(L10N.POST_WINDOW_ENTRY_MENU_NAME.s(), null,
            previewEntryAction,
            null,
            publishAction,
            publishAndContinueAction,
            publishLaterAction,
            null,
            deleteEntryAction
        );
    }

    @Override
    protected JMenu createFileMenu() {
        return createMenu(L10N.MAIN_FRAME_MENU_FILE.s(), null,
            getConnectAction(),
            null,
            new OpenAction(),
            new SaveAction(),
            new SaveAsAction(),
            (OSUtil.isMacOSX())? BaseFrame.VOID : null,
            (OSUtil.isMacOSX())? BaseFrame.VOID : new ExitAction()
        );
    }

    private void onPostDeletedEvent(PostDeletedEvent event) {
        if (event.getPost().equals(getPost())) {
            // close frame or make it empty
            final int count = getProfile().getFramesCount(this.getClass());
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    if (count > 1) {
                        closeFrame(true);
                    } else {
                        setEmptyPost();
                    }
                }
            });
        }
    }

    @Override
    protected boolean canClose() {
        int count = getProfile().getFramesCount(this.getClass());
        if (count == 1) {
            if (MainClass.showConfirmDialog(this)) {
                // Close all profile related frames
                getProfile().sendApplicationEvent(new CloseFrameEvent(this));
                getProfile().setConnected(false);
                return true;
            }
            return false;
        }

        return super.canClose();
    }

    public void afterPostSent() {
        int count = getProfile().getFramesCount(this.getClass());
        if (count > 1) {
            closeFrame(true);
        } else {
            setEmptyPost();
        }
    }

    public AbstractAction getDeleteEntryAction() {
        return deleteEntryAction;
    }

    public AbstractAction getPreviewEntryAction() {
        return previewEntryAction;
    }

    public AbstractAction getPublishAction() {
        return publishAction;
    }

    public AbstractAction getPublishAndContinueAction() {
        return publishAndContinueAction;
    }

    public AbstractAction getPublishLaterAction() {
        return publishLaterAction;
    }

    public AbstractAction getUpdateTagsAction() {
        return updateTagsAction;
    }

    protected static String getPostWindowSubjectLabelText() {
        return L10N.POST_WINDOW_SUBJECT_LABEL.s();
    }

    protected static String getPostWindowDraftCheckBoxText() {
        return L10N.POST_WINDOW_DRAFT_CHECKBOX.s();
    }

    protected static String getManualDateCheckboxText() {
        return L10N.POST_WINDOW_MANUAL_DATE_CHECKBOX.s();
    }

    protected static String getPostWindowNewPostTitle() {
        return L10N.POST_WINDOW_TITLE_NEW_POST.s();
    }

    protected static String getPostWindowEditPostTitle() {
        return L10N.POST_WINDOW_TITLE_EDIT_POST.s();
    }

    protected void save(IBlogEntry post, File file) throws IOException {
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file))) {
            out.writeObject(post);
        }
    }

    @Override
    public void open() {
        JFileChooser d = new JFileChooser();
        d.addChoosableFileFilter(new PetrusBloggerFileFilter());

        if (d.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            final File file = d.getSelectedFile();

            BlogPostEditorFrame w = (BlogPostEditorFrame)WindowManager.find(BlogPostEditorFrame.class, new WindowManagerEnumerator<BlogPostEditorFrame>() {
                @Override
                public boolean frameFound(BlogPostEditorFrame frame) {
                    File f = frame.getSaveFile();
                    if (f != null) {
                        try {
                            return file.getCanonicalPath().equals(f.getCanonicalPath());
                        } catch (IOException ex) {
                            return false;
                        }
                    } else {
                        return false;
                    }
                }
            });

            if (w != null) {
                w.setVisible(true);
            } else {
                E post = null;

                try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) {
                    post = (E)in.readObject();
                    getProfile().openBlogEntryEditor(post, file);
                } catch (IOException | ClassNotFoundException ex) {
                    MainClass.handleException(this, ex);
                }
            }
        }
    }


    @Override
    public void save() {
        if (saveFile == null) {
            saveAs();
        } else {
            E post = createPostObject();
            if (post != null) {
                try {
                    post.clearId();
                    save(post, saveFile);
                } catch (Exception ex) {
                    MainClass.handleException(this, ex);
                }
            }
        }
    }

    @Override
    public void saveAs() {
        JFileChooser d = new JFileChooser();
        d.addChoosableFileFilter(new PetrusBloggerFileFilter());

        if (d.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
            javax.swing.filechooser.FileFilter filter = d.getFileFilter();
            File file = d.getSelectedFile();
            if ((filter != null) && (filter instanceof FileFilter)) {
                file = ((FileFilter)filter).appendExtension(file);
            }

            saveFile = file;
            save();
        }
    }

    protected abstract Object getPost();
    protected abstract void setEmptyPost();
    protected abstract E createPostObject();

    public abstract void onPublish();
    public abstract void onPublishAndContinue();
    public abstract void onPublishLater();
    public abstract void onPreviewEntry();
    public abstract void onDeleteEntry();
    public abstract void onUpdateTags();
}
